package com.easycoding.framework.repository.support.query;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Root;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.easycoding.framework.repository.support.query.expression.ValueExpression;

/**
 * 表达式
 * @author Wangjk
 * @Date 2013-9-27
 * @Version 1.0
 * @param <T>
 */
public abstract class QueryExpression<T> {
	
	private static final Log log = LogFactory.getLog(QueryExpression.class);
	
	
	/**
	 * 自定义函数
	 */
	List<Map<FunType,List<QueryExpression<?>>>> funs =  new ArrayList<Map<FunType,List<QueryExpression<?>>>>();
	
	/**
	 * 转化表达式
	 * @param root
	 * @param cb
	 * @return
	 * @throws NoSuchMethodException 
	 * @throws SecurityException 
	 * @throws InvocationTargetException 
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 */
	@SuppressWarnings("unchecked")
	public Expression<T> expression(Root<?> root, CriteriaBuilder cb){
		Expression<T> expression = this.toExpression(root, cb);
		if(funs != null && !funs.isEmpty()){//处理函数不会空
			Class<?> c = cb.getClass();
			if(log.isDebugEnabled())
				log.debug("表达式处理------");
			for(Map<FunType, List<QueryExpression<?>>> f : funs){
				for(Entry<FunType, List<QueryExpression<?>>> t : f.entrySet()){
					if(log.isDebugEnabled())
						log.debug("函数类型：" + t.getKey().toString());
					List<QueryExpression<?>> values = t.getValue();
					Object[] args = new Object[1 + (values != null ? values.size() : 0)] ;
					Class<?>[] parameterTypes = new Class<?>[1 + (values != null ? values.size() : 0)];
					args[0] = expression;
					parameterTypes[0] = Expression.class;
					if(values != null && values.size() > 0){
						for(int i = 0 ;i < values.size() ; i++){
							parameterTypes[i+1] = Expression.class;
							args[i+1] = values.get(i).toExpression(root, cb);
							if(log.isDebugEnabled())
								log.debug("参数：" + args[i+1].toString());
						}
					}
					try {
						Method method = c.getMethod(t.getKey().toString(), parameterTypes);
						expression = (Expression<T>)method.invoke(cb, args);
					} catch (NoSuchMethodException e) {
						log.error("转化表达式反射异常",e);
					} catch (IllegalAccessException e) {
						log.error("转化表达式反射异常",e);
					} catch (InvocationTargetException e) {
						log.error("转化表达式反射异常",e);
					}
				}
			}
		}
		return expression;
	}
	
	/**
	 * 获取表达式
	 * @param root
	 * @param cb
	 * @return
	 */
	public abstract Expression<T> toExpression(Root<?> root, CriteriaBuilder cb);

	/**
	 * 等于
	 * @param value 值
	 */
	public Criteria equal(Object value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(new ValueExpression(value));
		criteria.setMatchType(MatchType.equal);
		return criteria;
	}
	
	/**
	 * 等于
	 * @param value 表达式
	 */
	public Criteria equal(QueryExpression<?> value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(value);
		criteria.setMatchType(MatchType.equal);
		return criteria;
	}
	
	/**
	 * 不等于
	 * @param value 值
	 */
	public Criteria notEqual(Object value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(new ValueExpression(value));
		criteria.setMatchType(MatchType.notEqual);
		return criteria;
	}
	
	/**
	 * 不等于
	 * @param value 表达式
	 */
	public Criteria notEqual(QueryExpression<?> value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(value);
		criteria.setMatchType(MatchType.notEqual);
		return criteria;
	}
	
	/**
	 * 大于
	 * @param value 值
	 */
	public Criteria gt(Object value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(new ValueExpression(value));
		criteria.setMatchType(MatchType.greaterThan);
		return criteria;
	}
	
	/**
	 * 大于
	 * @param value 表达式
	 */
	public Criteria gt(QueryExpression<?> value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(value);
		criteria.setMatchType(MatchType.greaterThan);
		return criteria;
	}	
	
	/**
	 * 大于等于
	 * @param value 值
	 */
	public Criteria gte(Object value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(new ValueExpression(value));
		criteria.setMatchType(MatchType.greaterThanOrEqualTo);
		return criteria;
	}
	
	/**
	 * 大于等于
	 * @param value 表达式
	 */
	public Criteria gte(QueryExpression<?> value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(value);
		criteria.setMatchType(MatchType.greaterThanOrEqualTo);
		return criteria;
	}
	
	/**
	 * 小于
	 * @param value 值
	 */
	public Criteria lt(Object value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(new ValueExpression(value));
		criteria.setMatchType(MatchType.lessThan);
		return criteria;
	}
	
	/**
	 * 小于
	 * @param value 表达式
	 */
	public Criteria lt(QueryExpression<?> value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(value);
		criteria.setMatchType(MatchType.lessThan);
		return criteria;
	}	
	
	/**
	 * 小于等于
	 * @param value 值
	 */
	public Criteria lte(Object value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(new ValueExpression(value));
		criteria.setMatchType(MatchType.lessThanOrEqualTo);
		return criteria;
	}
	
	/**
	 * 小于等于
	 * @param value 表达式
	 */
	public Criteria lte(QueryExpression<?> value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(value);
		criteria.setMatchType(MatchType.lessThanOrEqualTo);
		return criteria;
	}
	
	/**
	 * 模糊匹配
	 * @param value 值
	 */
	public Criteria like(Object value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(new ValueExpression(value));
		criteria.setMatchType(MatchType.like);
		return criteria;
	}
	
	/**
	 * 模糊匹配
	 * @param value 表达式
	 */
	public Criteria like(QueryExpression<?> value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(value);
		criteria.setMatchType(MatchType.like);
		return criteria;
	}
	
	/**
	 * 模糊匹配
	 * @param value 值
	 */
	public Criteria notLike(Object value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(new ValueExpression(value));
		criteria.setMatchType(MatchType.notLike);
		return criteria;
	}
	
	/**
	 * 模糊匹配
	 * @param value 表达式
	 */
	public Criteria notLike(QueryExpression<?> value){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(value);
		criteria.setMatchType(MatchType.notLike);
		return criteria;
	}
	
	/**
	 * 为空
	 */
	public Criteria isNull(){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.isNull);
		return criteria;
	}
	
	/**
	 * 不为空
	 */
	public Criteria isNotNull(){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.isNotNull);
		return criteria;
	}
	
	/**
	 * 为真
	 */
	public Criteria isTrue(){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.isTrue);
		return criteria;
	}
	
	/**
	 * 为假
	 */
	public Criteria isFalse(){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.isFalse);
		return criteria;
	}
	
	/**
	 * between查询
	 * @param value1 值1
	 * @param value2 值2
	 */
	public Criteria between(Object value1,Object value2){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(new ValueExpression(value1));
		criteria.getValues().add(new ValueExpression(value2));
		criteria.setMatchType(MatchType.between);
		return criteria;
	}
	
	/**
	 * between查询
	 * @param field
	 * @param value
	 * @return
	 */
	public Criteria between(QueryExpression<?> value1,QueryExpression<?> value2 ){
		Criteria criteria = new Criteria(this);
		criteria.getValues().add(value1);
		criteria.getValues().add(value2);
		criteria.setMatchType(MatchType.between);
		return criteria;
	}
	
	/**
	 * in查询
	 * @param values 值集合
	 * @return
	 */
	public Criteria in(Collection<?> values){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.in);
		if(values != null && !criteria.getValues().isEmpty()){
			for(Object value : values){
				criteria.getValues().add(new ValueExpression(value));
			}
		}
		return criteria;
	}
	
	/**
	 * in查询
	 * @param values 值
	 * @return
	 */
	public Criteria in(Object... values){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.in);
		if(values != null){
			for(Object value : values){
				criteria.getValues().add(new ValueExpression(value));
			}
		}
		return criteria;
	}
	
	/**
	 * in查询
	 * @param values 值表达式
	 * @return
	 */
	public Criteria in(QueryExpression<?>... values){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.in);
		if(values != null)
			criteria.getValues().addAll(Arrays.asList(values));
		return criteria;
	}
	
	/**
	 * notIn查询
	 * @param values 值集合
	 * @return
	 */
	public Criteria notIn(Collection<?> values){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.in);
		if(values != null && !criteria.getValues().isEmpty()){
			for(Object value : values){
				criteria.getValues().add(new ValueExpression(value));
			}
		}
		criteria.not();
		return criteria;
	}
	
	/**
	 * notIn查询
	 * @param values 值
	 * @return
	 */
	public Criteria notIn(Object... values){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.in);
		if(values != null){
			for(Object value : values){
				criteria.getValues().add(new ValueExpression(value));
			}
		}
		criteria.not();
		return criteria;
	}
	
	/**
	 * notIn查询
	 * @param values 值表达式
	 * @return
	 */
	public Criteria notIn(QueryExpression<?>... values){
		Criteria criteria = new Criteria(this);
		criteria.setMatchType(MatchType.in);
		if(values != null)
			criteria.getValues().addAll(Arrays.asList(values));
		criteria.not();
		return criteria;
	}
	
	/**
	 * 绝对值
	 * @return
	 */
	public QueryExpression<?> abs(){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		fun.put(FunType.abs, null);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 大写
	 * @return
	 */
	public QueryExpression<?> upper(){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		fun.put(FunType.upper, null);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 小写
	 * @return
	 */
	public QueryExpression<?> lower(){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		fun.put(FunType.lower, null);
		funs.add(fun);
		return this;
	}
	
	/** 
	 * 联接字符串
	 * @param arg1 字符串
	 * @return
	 */
	public QueryExpression<?> concat(String arg1){
		return this.concat(new ValueExpression(arg1));
	}
	
	/**
	 * 联接表达式
	 * @param arg1 表达式
	 * @return
	 */
	public QueryExpression<?> concat(QueryExpression<?> arg1){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(arg1);
		fun.put(FunType.concat, values);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 去空格
	 * @return
	 */
	public QueryExpression<?> trim(){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		fun.put(FunType.trim, null);
		funs.add(fun);
		return this;
	}
	
	/**
	 * null处理
	 * @param arg1
	 * @return
	 * 如果表达式为null，则返回arg1，否则返回表达式
	 */
	public QueryExpression<?> coalesce(Object arg1){
		return this.coalesce(new ValueExpression(arg1));
	}
	
	/**
	 * null处理
	 * @param arg1 表达式
	 * @return
	 * 如果表达式为null，则返回arg1，否则返回原表达式
	 */
	public QueryExpression<?> coalesce(QueryExpression<?> arg1){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(arg1);
		fun.put(FunType.coalesce, values);
		funs.add(fun);
		return this;
	}
	
	/**
	 * nullif处理
	 * @param arg1
	 * @return
	 * 如果表达式与值arg1相等，则返回null，否则返回原表达式
	 */
	public QueryExpression<?> nullif(Object arg1){
		return this.nullif(new ValueExpression(arg1));
	}
	
	/**
	 * nullif
	 * @param arg1 表达式
	 * @return
	 * 如果表达式与表达式arg1相等，则返回null，否则返回原表达式
	 */
	public QueryExpression<?> nullif(QueryExpression<?> arg1){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(arg1);
		fun.put(FunType.nullif, values);
		funs.add(fun);
		return this;
	} 
	
	/**
	 * 长度
	 * @return
	 */
	public QueryExpression<?> length(){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		fun.put(FunType.length, null);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 字符串截取
	 * @param start 截取开始位置，从1开始计算
	 * @return
	 */
	public QueryExpression<?> substring(int start){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(new ValueExpression(start));
		fun.put(FunType.substring, values);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 字符串截取
	 * @param start 截取开始位置，从1开始计算
	 * @param length 截取长度
	 * @return
	 */
	public QueryExpression<?> substring(int start,int length){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(new ValueExpression(start));
		values.add(new ValueExpression(length));
		fun.put(FunType.substring, values);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 加
	 * @param number 数字
	 * @return
	 */
	public QueryExpression<?> add(Number number){
		return this.add(new ValueExpression(number));
	}
	
	/**
	 * 加
	 * @param arg1 表达式
	 * @return
	 */
	public QueryExpression<?> add(QueryExpression<?> arg1){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(arg1);
		fun.put(FunType.sum, values);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 减
	 * @param number 数字
	 * @return
	 */
	public QueryExpression<?> diff(Number number){
		return this.diff(new ValueExpression(number));
	}
	
	/**
	 * 减
	 * @param arg1 表达式
	 * @return
	 */
	public QueryExpression<?> diff(QueryExpression<?> arg1){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(arg1);
		fun.put(FunType.diff, values);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 乘
	 * @param number 数字
	 * @return
	 */
	public QueryExpression<?> prod(Number number){
		return this.prod(new ValueExpression(number));
	}
	
	/**
	 * 乘
	 * @param arg1 表达式
	 * @return
	 */
	public QueryExpression<?> prod(QueryExpression<?> arg1){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(arg1);
		fun.put(FunType.prod, values);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 除
	 * @param number 数字
	 * @return
	 */
	public QueryExpression<?> quot(Number number){
		return this.quot(new ValueExpression(number));
	}
	
	/**
	 * 除
	 * @param arg1 表达式
	 * @return
	 */
	public QueryExpression<?> quot(QueryExpression<?> arg1){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(arg1);
		fun.put(FunType.quot, values);
		funs.add(fun);
		return this;
	}
	
	/**
	 * 求余
	 * @param number 数字
	 * @return
	 */
	public QueryExpression<?> mod(int number){
		return this.mod(new ValueExpression(number));
	}
	
	/**
	 * 求余
	 * @param arg1 表达式
	 * @return
	 */
	public QueryExpression<?> mod(QueryExpression<?> arg1){
		Map<FunType,List<QueryExpression<?>>> fun  = new HashMap<FunType,List<QueryExpression<?>>>();
		List<QueryExpression<?>> values = new ArrayList<QueryExpression<?>>();
		values.add(arg1);
		fun.put(FunType.mod, values);
		funs.add(fun);
		return this;
	}
	
	
}
